(function () {
	var ns = ij.pkg('core').namespaceString;
	ij.core.Animation = ij.core.Observable.extend({
		init: function (config) {
			return this._super(ij.apply({
				classname: ns+'.Animation',
				name: null,
				instructions: [],
				currentInstruction: 0,
				animatedObject: null,
				running: false,
				paused: false,
				loop: 1,
				currentLoop: 0
			}, config));
		},
		apply: function (extension, overwrite_existing) {
			var instructions = [],
			config = ij.clone(extension);
			if (config && config.instructions) {
				instructions = config.instructions;
				if (this.instructions) {
					delete config.instructions;
				} else {
					config.instructions = [];
				}
			}
			
			this._super(config, overwrite_existing);
			
			instructions.each(function (i, item) {
				this.addInstruction(item);
			}, this);
		},
		startInstruction: function (instruction) {
			instruction = instruction || this.instructions[this.currentInstruction];
			instruction.start();
			
			return this;
		},
		nextInstruction: function () {
			this.currentInstruction++;
			if (this.currentInstruction == this.instructions.length) {
				this.currentLoop++;
				if (this.currentLoop == this.loop) {
					this.stop();
				} else {
					this.currentInstruction = 0;
					this.startInstruction();
				}
			} else {
				this.startInstruction();
			}
			
			return this;
		},
		start: function () {
			if (this.running) {
				this.stop();
			}
			this.currentLoop = 0;
			this.running = true;
			this.currentInstruction = 0;
			this.dispatchEvent('start');
			this.startInstruction();
			
			return this;
		},
		stop: function () {
			if (this.running) {
				this.running = false;
				if (this.instructions[this.currentInstruction]) {
					this.instructions[this.currentInstruction].stop();
				}
				this.currentInstruction = 0;
				this.dispatchEvent('stop');
			}
			
			return this;
		},
		pause: function () {
			if (this.running && !this.paused) {
				this.instructions[this.currentInstruction].pause();
				this.dispatchEvent('pause');
			}
		},
		resume: function () {
			if (this.running && this.paused) {
				this.instructions[this.currentInstruction].resume();
				this.dispatchEvent('resume');
			}
		},
		addInstruction: function (instruction) {
			if (!(instruction instanceof ij.core.AnimationInstruction)) {
				instruction = new (ij.namespace(instruction.classname))(ij.apply(ij.clone(instruction), {
					animation: this
				}));
			}
			instruction.bind('stop', function () {
				if (this.running) {
					this.nextInstruction();
				}
			}, this);
			this.instructions.push(instruction);
			
			return this;
		},
		step: function () {
			if (this.running && !this.paused) {
				this.instructions[this.currentInstruction].step();
				this.dispatchEvent('step');
			}
			
			return this;
		}
	});
})();
